package com.hundsun.cprs.yyt.service.match.core.stat;

import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * 交易撮合系统纪录份额以及对应的数据结构
 * 
 */
public class TradesQuotes {

    private Log logger  = LogFactory.getLog(this.getClass());
    private AtomicInteger tradeSequence = new AtomicInteger(0); //当前成交序列号
    private AtomicLong remarkSequence = new AtomicLong(0);      //当前配号序列号
    private AtomicLong sellEntrustAmount = new AtomicLong(0);   //挂卖商品数量
    private AtomicLong buyEntrustAmount = new AtomicLong(0);    //挂买商品数量
    private Map<String, MemberTradesStatistics> memberStatistics      = new ConcurrentHashMap<String, MemberTradesStatistics>();
    private Map<String, AtomicLong>             stockHoldStatistics   = new ConcurrentHashMap<String, AtomicLong>();
    private Map<String, AtomicLong>             stockBuyingStatistics = new ConcurrentHashMap<String, AtomicLong>();

    public TradesQuotes() {
        super();
    }

    public Map<String, AtomicLong> getStockBuyingStatistics() {
        return stockBuyingStatistics;
    }

    public Map<String, AtomicLong> getStockHoldStatistics() {
        return stockHoldStatistics;
    }

    public Long getStockHoldByAccount(String fundAccount) {
        AtomicLong amount = stockHoldStatistics.get(fundAccount);
        return amount == null ? 0 : amount.get();
    }

    public Long getStockBuyingByAccount(String fundAccount) {
        AtomicLong amount = stockBuyingStatistics.get(fundAccount);
        return amount == null ? 0 : amount.get();
    }

    /**
     * 持仓者+已委买者总共的数量(持仓和委买重复的账号算一个)
     * @return
     */
    public int getTotalHolderAndBuying() {
        Set<String> set = new HashSet<String>();
        set.addAll(stockHoldStatistics.keySet());
        set.addAll(stockBuyingStatistics.keySet());
        return set.size();
    }

    public int getBuyingTotal() {
        return stockBuyingStatistics.size();
    }
    
    /**
     * 获取商品持仓总人数
     * @return
     */
    public int getHolderTotal() {
        return stockHoldStatistics.size();
    }

    /**
     * 是否已经是持有者或者正在委买
     * @param fundAccount
     * @return
     */
    public boolean isHolderOrBuyerYet(String fundAccount) {
        return stockHoldStatistics.containsKey(fundAccount)
               || stockBuyingStatistics.containsKey(fundAccount);
    }

    /**
     * 买成交后的持仓变化：
     * 1.如果是新股东，新增元素
     * 2.持仓累加
     * @param fundAccount
     * @param amount
     */
    public void boughtInStockHold(String fundAccount, Long amount) {
        if (fundAccount == null) {
            throw new NullPointerException("fundAccount can't be null.");
        }
        if (amount == null) {
            throw new NullPointerException("entrustAmount can't be null.");
        }
        AtomicLong holdCount = stockHoldStatistics.get(fundAccount);
        AtomicLong buyingCount = stockBuyingStatistics.get(fundAccount);
        synchronized (this) {
            if (buyingCount != null) {
                long leftBuying = buyingCount.addAndGet(-amount);
                if (leftBuying <= 0)
                    stockBuyingStatistics.remove(fundAccount);
            }
            if (holdCount == null) {
                holdCount = new AtomicLong(0);
                stockHoldStatistics.put(fundAccount, holdCount);
            }
        }
        holdCount.addAndGet(amount);
    }

    /**
     * 新增买挂单数量
     * @param fundAccount
     * @param amount
     */
    public void addBuyingStockHold(String fundAccount, Long amount) {
        if (fundAccount == null) {
            throw new NullPointerException("fundAccount can't be null.");
        }
        if (amount == null) {
            throw new NullPointerException("entrustAmount can't be null.");
        }
        AtomicLong buyingCount = stockBuyingStatistics.get(fundAccount);
        synchronized (this) {
            if (buyingCount == null) {
                buyingCount = new AtomicLong(0);
                stockBuyingStatistics.put(fundAccount, buyingCount);
            }
        }
        buyingCount.addAndGet(amount);
    }

    /**
     * 减少买挂单数量
     * @param fundAccount
     * @param amount
     */
    public void reduceBuyingStockHold(String fundAccount, Long amount) {
        if (fundAccount == null) {
            throw new NullPointerException("fundAccount can't be null.");
        }
        if (amount == null) {
            throw new NullPointerException("entrustAmount can't be null.");
        }
        AtomicLong buyingCount = stockBuyingStatistics.get(fundAccount);
        synchronized (this) {
            if (buyingCount != null) {
                long leftBuying = buyingCount.addAndGet(-amount);
                if (leftBuying <= 0)
                    stockBuyingStatistics.remove(fundAccount);
            }
        }
    }

    /**
     * 卖成交后的持仓变化
     * 1.减去持仓
     * 2.如果卖出后持仓为0，则去除该账号
     * @param fundAccount
     * @param amount
     */
    public void soldOutStockHold(String fundAccount, Long amount) {
        if (fundAccount == null) {
            throw new NullPointerException("fundAccount can't be null.");
        }
        if (amount == null) {
            throw new NullPointerException("entrustAmount can't be null.");
        }
        AtomicLong holdCount = stockHoldStatistics.get(fundAccount);
        synchronized (this) {
            if (holdCount != null) {
                long result = holdCount.addAndGet(-amount);
                if (result <= 0) {
                    stockHoldStatistics.remove(fundAccount);
                }
            }
        }
    }

    /**
     * 得到当前序号
     * 
     * @return
     */
    public int getCurrentTradeSeq() {
        return this.tradeSequence.get();
    }

    /**
     * 当前序号+1返回
     * 
     * @return
     */
    public int getTradeSequence() {
        return this.tradeSequence.incrementAndGet();
    }

    /**
     * 当前序号+n返回
     * 
     * @param n
     * @return
     */
    public int getTradeSequence(int n) {
        return this.tradeSequence.addAndGet(n);
    }

    /**
     * 设置当前序号为n
     * 
     * @param n
     */
    public void setTradeSequence(int n) {
        this.tradeSequence.set(n);
    }

    /**
     * 如果当前tradeSequence比n小,则设置为n
     * 
     * @param n
     */
    public void setTradeSequenceIfLess(int n) {
        if (this.tradeSequence.get() < n) {
            this.tradeSequence.set(n);
        }
    }

    /**
     * 得到当前序号
     * 
     * @return
     */
    public long getCurrentRemarkSeq() {
        return this.remarkSequence.get();
    }

    /**
     * 当前序号+1返回
     * 
     * @return
     */
    public long getRemarkSequence() {
        return this.remarkSequence.incrementAndGet();
    }

    /**
     * 当前序号+n返回
     * 
     * @param n
     * @return
     */
    public long getRemarkSequence(long n) {
        return this.remarkSequence.addAndGet(n);
    }

    /**
     * 设置当前序号为n
     * 
     * @param n
     */
    public void setRemarkSequence(long n) {
        this.remarkSequence.set(n);
    }

    public MemberTradesStatistics getTradesStatistics(String fundAccount) {
        MemberTradesStatistics statiitics = memberStatistics.get(fundAccount);
        if (statiitics == null) {
            synchronized (this) {
                MemberTradesStatistics create = new MemberTradesStatistics();
                statiitics = memberStatistics.get(fundAccount);// 双重检查
                if (statiitics == null) {
                    memberStatistics.put(fundAccount, create);
                    statiitics = create;
                }
            }
        }
        return statiitics;
    }

    /**
     * 增加未买(挂单)份额
     * 
     * @param fundAccount
     * @param entrustAmount
     */
    public void addBuyingAmount(String fundAccount, Long entrustAmount) {
        if (fundAccount == null) {
            throw new NullPointerException("fundAccount can't be null.");
        }
        if (entrustAmount == null) {
            throw new NullPointerException("entrustAmount can't be null.");
        }
        //getTradesStatistics(fundAccount).buyEntrust(entrustAmount);
        buyEntrustAmount.addAndGet(entrustAmount);
    }

    /**
     * 减少未买(挂单)数量
     * @param fundAccount
     * @param entrustAmount
     */
    public void reduceBuyingAmount(String fundAccount, Long entrustAmount) {
        if (fundAccount == null) {
            throw new NullPointerException("fundAccount can't be null.");
        }
        if (entrustAmount == null) {
            throw new NullPointerException("entrustAmount can't be null.");
        }
        //getTradesStatistics(fundAccount).reduceBuyEntrust(entrustAmount);
        //reduceBuyingStockHold(fundAccount, entrustAmount);
        buyEntrustAmount.addAndGet(-1 * entrustAmount);
    }

    /**
     * 增加卖单份额
     * 
     * @param fundAccount
     * @param entrustAmount
     */
    public void addSellingAmount(String fundAccount, Long entrustAmount) {
        if (fundAccount == null) {
            throw new NullPointerException("fundAccount can't be null.");
        }
        if (entrustAmount == null) {
            throw new NullPointerException("entrustAmount can't be null.");
        }
        //getTradesStatistics(fundAccount).sellEntrust(entrustAmount);
        sellEntrustAmount.addAndGet(entrustAmount);
    }

    /**
     * 减少卖单份额
     * 
     * @param fundAccount
     * @param entrustAmount
     */
    public void reduceSellingAmount(String fundAccount, Long entrustAmount) {
        if (fundAccount == null) {
            throw new NullPointerException("fundAccount can't be null.");
        }
        if (entrustAmount == null) {
            throw new NullPointerException("entrustAmount can't be null.");
        }
        //getTradesStatistics(fundAccount).reduceSellEntrust(entrustAmount);
        sellEntrustAmount.addAndGet(-1 * entrustAmount);
    }

    /**
     * 买订单撮合成功,增加成交份额,减少挂单份额
     * 
     * @param fundAccount
     * @param amount
     */
    public void boughtInStock(String fundAccount, Long amount) {
        if (fundAccount == null) {
            throw new NullPointerException("fundAccount can't be null.");
        }
        if (amount == null) {
            throw new NullPointerException("entrustAmount can't be null.");
        }
        getTradesStatistics(fundAccount).boughtIn(amount);
        // 买入成交后更新持仓信息
        boughtInStockHold(fundAccount, amount);
    }

    /**
     * 买订单撮合成功,增加成交份额,减少挂单份额
     * 第三方付款方式
     * @param fundAccount
     * @param amount
     */
    public void boughtInStockOfThird(String fundAccount, Long amount) {
        if (fundAccount == null) {
            throw new NullPointerException("fundAccount can't be null.");
        }
        if (amount == null) {
            throw new NullPointerException("entrustAmount can't be null.");
        }
        getTradesStatistics(fundAccount).boughtInByThird(amount);
        // 买入成交后更新持仓信息
        boughtInStockHold(fundAccount, amount);
    }
    
    /**
     * 卖定撮合成功,增加成交份额,减少挂单份额
     * 
     * @param fundAccount
     * @param amount
     */
    public void soldOutStock(String fundAccount, Long amount) {
        if (fundAccount == null) {
            throw new NullPointerException("fundAccount can't be null.");
        }
        if (amount == null) {
            throw new NullPointerException("entrustAmount can't be null.");
        }
        getTradesStatistics(fundAccount).soldOut(amount);
        // 卖出成交后更新持仓信息
        soldOutStockHold(fundAccount, amount);
    }

    /**
     * 卖定撮合成功,增加成交份额,减少挂单份额
     *  第三方付款方式
     * @param fundAccount
     * @param amount
     */
    public void soldOutStockOfThird(String fundAccount, Long amount) {
        if (fundAccount == null) {
            throw new NullPointerException("fundAccount can't be null.");
        }
        if (amount == null) {
            throw new NullPointerException("entrustAmount can't be null.");
        }
        getTradesStatistics(fundAccount).soldOutOfThird(amount);
        // 卖出成交后更新持仓信息
        soldOutStockHold(fundAccount, amount);
    }
    /**
     * 删除所有数据，重新构造
     */
    public void clean() {
        tradeSequence.set(0);
        memberStatistics.clear();
    }

    /**
     * 清理用户统计数据,注意,一般只应该用于开发测试环境
     */
    public void cleanMemberStatistics() {
        memberStatistics.clear();
    }

    public AtomicLong getSellEntrustAmount() {
        return sellEntrustAmount;
    }

    public AtomicLong getBuyEntrustAmount() {
        return buyEntrustAmount;
    }

}
